package service;

import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;

import rmiClient.ManagementClientCallback;

public class SubscriptionHolder {

	private final String[] types = {"AUCTION_STARTED", //0
			"AUCTION_ENDED", //1
			"USER_LOGIN", //2
			"USER_LOGOUT", //3
			"USER_DISCONNECTED", //4
			"BID_PLACED", //5
			"BID_OVERBID", //6
			"BID_WON", //7
			"USER_SESSIONTIME_MIN", //8
			"USER_SESSIONTIME_MAX", //9
			"USER_SESSIONTIME_AVG", //10
			"BID_PRICE_MAX", //11
			"BID_COUNT_PER_MINUTE", //12
			"AUCTION_TIME_AVG", //13
	"AUCTION_SUCCESS_RATIO"}; //14
	private HashMap<ManagementClientCallback,int[]> subscriptionsAdd = new HashMap<ManagementClientCallback,int[]>();
	private HashMap<String,ArrayList<ManagementClientCallback>> subscriptionsNotify = new HashMap<String,ArrayList<ManagementClientCallback>>();
	private ArrayList<String> subscriptionsID = new ArrayList<String>();
	private static int idcounter = 0;
	private static SubscriptionHolder instance;
	private static final Object singletonObject = new Object();


	private SubscriptionHolder() {
		for(int i=0;i<types.length;i++) {
			subscriptionsNotify.put(types[i], new ArrayList<ManagementClientCallback>());
		}

	}
	

	
	public static SubscriptionHolder getInstance() {
		if(instance == null) {
			synchronized(singletonObject) {
				if(instance == null) instance = new SubscriptionHolder();
			}
		}
		return instance;
	}

	private int[] getNewSubAmountArray() {
		int[] subAmount = new int[15];
		for(int i=0;i<subAmount.length;i++) {
			subAmount[i] = 0;
		}
		return subAmount;
	}

	public String addSubscription(String filter, ManagementClientCallback client) {
		int[] subAmount;
		String id=""+idcounter;
		subscriptionsID.add(filter+" "+extractPort(client.toString()));
		idcounter++;
		String in = filter.replace('|', '-');
		String[] filtertypes = in.split("-");

		for(int i=0;i<filtertypes.length;i++) {
			filtertypes[i] = filtertypes[i].replaceAll("[^a-zA-Z_]", ""); // TODO fix possible error
			filtertypes[i].trim();

			for(int j=0;j<types.length;j++) {
				if(types[j].startsWith(filtertypes[i])) {

					synchronized(this) {
						if(subscriptionsAdd.containsKey(client)) {
							subscriptionsAdd.get(client)[j]++;
						} else {
							subAmount = getNewSubAmountArray();
							subAmount[j]++;
							subscriptionsAdd.put(client,subAmount);
						}

						if(subscriptionsAdd.get(client)[j]==1) {
							subscriptionsNotify.get(types[j]).add(client);
						}
					}

				}
			}
		}
		return id;
	}

	public int deleteSubscription(String id){
		int intId = -1;

		try {
			intId = Integer.parseInt(id);
		} catch (NumberFormatException e) {
			e.printStackTrace();
		}

		if(intId==-1) return intId;



		if(subscriptionsID.get(intId)!="") {
			String[] substrings = subscriptionsID.get(intId).split(" "); //TODO unsafe, client A could terminate subscriptions of client B
			String filter = substrings[0];
			String port = substrings[1];

			String in = filter.replace('|', '-');
			String[] filtertypes = in.split("-");
			for(int i=0;i<filtertypes.length;i++) {
				filtertypes[i] = filtertypes[i].replaceAll("[^a-zA-Z_]", "");
				filtertypes[i].trim();
				for(int j=0;j<types.length;j++) {
					if(types[j].startsWith(filtertypes[i])) {
						synchronized(this) {
							Set<ManagementClientCallback> keys = subscriptionsAdd.keySet();
							Iterator<ManagementClientCallback> it = keys.iterator();
							while(it.hasNext()) {
								ManagementClientCallback mcc = it.next();
								if(extractPort(mcc.toString()).equals(port)) {
									if(subscriptionsAdd.get(mcc)[j]>0) {
										subscriptionsAdd.get(mcc)[j]--;
									}
									if(subscriptionsAdd.get(mcc)[j]==0) {
										subscriptionsNotify.get(types[j]).remove(mcc);
									}
								}
							}
						}
					}
				}
			}
		}

		subscriptionsID.set(intId,"");
		return intId;
	}


	public void notify(Event e) throws RemoteException{

		synchronized(this) {
			ArrayList<ManagementClientCallback> subs = subscriptionsNotify.get(e.getType());
			for(int i=0;i<subs.size();i++){
				subs.get(i).processEvent(e);
			}
		}
	}

	private String extractPort(String clientToString) {
		//Proxy[ManagementClientCallback,RemoteObjectInvocationHandler[UnicastRef [liveRef: [endpoint:[192.168.0.17:12112](remote),objID:[5c2015c2:13b37245260:-7fff, -8798547678857718177]]]]]
		String port = clientToString.split(":")[3];
		port = port.split("]")[0];
		return port;
	}
}